﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using PGMHelper;

namespace Spectrum
{
    [JsonObject(MemberSerialization.OptIn)]
    /// <summary>
    /// 单质点响应集合
    /// </summary>
    public class ResponseSet
    {
        #region Variables

        [JsonProperty]
        /// <summary>
        /// 响应时程
        /// </summary>
        private List<ResponseParam> responseSet { set; get; }

        /// <summary>
        /// 加速度激励时程
        /// </summary>
        private List<AccelParam> accelSet { set; get; }

        [JsonProperty]
        /// <summary>
        /// 周期
        /// </summary>
        public float Period { set; get; }

        [JsonProperty]
        /// <summary>
        /// 阻尼比
        /// </summary>
        public float DampR { set; get; }

        [JsonProperty]
        /// <summary>
        /// 模型质量
        /// </summary>
        public float Mass { set; get; }

        #endregion

        #region Properties

        /// <summary>
        /// 获得某时刻的响应
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public ResponseParam this[int index]
        {
            get
            {
                return index >= this.responseSet.Count? 
                    new ResponseParam(): this.responseSet[index];
            }
        }

        /// <summary>
        /// 响应点数目
        /// </summary>
        public int Count
        {
            get
            {
                return this.responseSet.Count;
            }
        }

        /// <summary>
        /// 获得位移响应最大值
        /// </summary>
        public float Umax
        {
            get
            {
                return this.GetMaxValue("U");
            }
        }

        /// <summary>
        /// 影响系数最大值
        /// </summary>
        private float Afamax
        {
            get
            {
                return this.GetMaxValue("A");
            }
        }

        /// <summary>
        /// 速度响应最大值
        /// </summary>
        private float Vmax
        {
            get
            {
                return this.GetMaxValue("V");
            }
        }

        /// <summary>
        /// 获得最后一刻的响应
        /// </summary>
        public ResponseParam Last
        {
            get
            {
                return this.responseSet.Last();
            }
        }

        /// <summary>
        /// 时间间隔（仅等时长可用）
        /// </summary>
        private float deltaDt
        {
            get
            {
                return this[1].Time - this[0].Time;
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// 将绝对加速度转为相对加速度
        /// </summary>
        public void SetRelativeAccel()
        {
            //遍历响应时程
            for(int index = 0; index < this.Count; index++)
                this.responseSet[index].A += this.accelSet[index].Accel;
        }

        /// <summary>
        /// 将加速度转为拟加速度
        /// </summary>
        public void ToPesudo()
        {
            //基本参数
            float wn = SpectrumMethod.Getwn(this.Period);
            //遍历计算
            this.responseSet.ForEach(response => response.ToPesudo(wn));
        }

        /// <summary>
        /// 放大响应
        /// </summary>
        /// <param name="factor"></param>
        public void Amplitude(float factor)
        {
            this.responseSet.ForEach(response => response.Amplitude(factor));
        }

        /// <summary>
        /// 添加SRSS分析结果
        /// </summary>
        /// <param name="paramX"></param>
        /// <param name="paramY"></param>
        private void Add(ResponseParam paramX, ResponseParam paramY)
        {
            this.responseSet.Add(new ResponseParam(paramX, paramY));
        }

        /// <summary>
        /// 分段解析法
        /// </summary>
        private void PiecewiseExcitationFunction(float integarteDt)
        {
            //初始化
            this.responseSet = new List<ResponseParam>(this.accelSet.Count);
            //基本参数
            float wn = SpectrumMethod.Getwn(this.Period);
            float wd = SpectrumMethod.Getwd(this.Period, this.DampR);
            float K = this.Mass * (float)Math.Pow(wn, 2);
            //计算参数值
            var kwdh = 1 / (K * wd * integarteDt);
            var beta = this.DampR * wn;
            var coswdh = Math.Cos(wd * integarteDt);
            var sinwdh = Math.Sin(wd * integarteDt);
            var ebetah = Math.Pow(Math.E, -beta * integarteDt);
            var beta2wdwn = 2 * beta * wd / Math.Pow(wn, 2);

            //A参数1：(((wd^2 - beta^2) / wn^2) - betah) * sin wd h
            var AFactor1 = ((Math.Pow(wd, 2) - Math.Pow(beta, 2)) / Math.Pow(wn, 2) - beta * integarteDt) * sinwdh;
            //A参数2：((2wd beta/ wn ^ 2) + wd h) * cos wd h
            var AFactor2 = (2 * wd * beta / Math.Pow(wn, 2) + wd * integarteDt) * coswdh;
            //A
            var A = kwdh * (ebetah * (AFactor1 - AFactor2) + beta2wdwn);
            //B参数1：(((wd^2 - beta^2) / wn^2)) * sin wd h
            var BFactor1 = (Math.Pow(wd, 2) - Math.Pow(beta, 2)) / Math.Pow(wn, 2) * sinwdh;
            //B参数2：((2wd beta/ wn ^ 2)) * cos wd h
            var BFactor2 = (2 * wd * beta / Math.Pow(wn, 2)) * coswdh;
            //B参数
            var B = kwdh * (ebetah * (-BFactor1 + BFactor2)  + wd * integarteDt - beta2wdwn);
            //C参数
            var C = ebetah * (coswdh + (beta / wd) * sinwdh);
            //D参数
            var D = (1 / wd) * ebetah * sinwdh;
            //参数AA
            var AAFactor1 = (beta + Math.Pow(wn, 2) * integarteDt) * sinwdh;
            var AAFactor2 = wd * coswdh;
            var AA = kwdh * (ebetah * (AAFactor1 + AAFactor2) - wd);
            //参数BB
            var BB = kwdh * (-ebetah * (beta * sinwdh + wd * coswdh) + wd);
            var CC = -(Math.Pow(wn, 2) / wd) * ebetah * sinwdh;
            var DD = ebetah * (coswdh - beta / wd * sinwdh);
            //初始状态
            double ust = 0; double vst = 0; var pist = 0.0;
            //初始化参数
            foreach (var accelParam in this.accelSet)
            {
                //防止质量为零
                if (this.Mass == 0)
                {
                    this.responseSet.Add(new ResponseParam(accelParam.Time, 0, 0, 0));
                    continue;
                }
                //获得值
                var preu = ust; var prev = vst; var prepi = pist;
                //计算值
                pist = accelParam.Accel;
                ust = A * prepi + B * pist + C * preu + D * prev;
                vst = AA * prepi + BB * pist + CC * preu + DD * prev;
                this.responseSet.Add(new ResponseParam(accelParam.Time, (float)ust, (float)(ust * Math.Pow(wn, 2)), (float)vst));
            }
        }

        /// <summary>
        /// 杜哈梅积分
        /// </summary>
        private void Duhamel()
        {
            //初始化
            this.responseSet = new List<ResponseParam>(this.accelSet.Count);
            //基本参数
            float wn = SpectrumMethod.Getwn(this.Period);
            float wd = SpectrumMethod.Getwd(this.Period, this.DampR);
            //初始化参数
            foreach (var accelParam in this.accelSet)
            {
                //初始化
                float pret = 0; double disp = 0;
                //遍历
                foreach (var subAccelParam in this.accelSet)
                {
                    //越界退出
                    if (subAccelParam.Time > accelParam.Time) break;
                    double dt = subAccelParam.Time - pret;
                    disp += subAccelParam.Accel *
                    Math.Exp(-1 * DampR * wn * (accelParam.Time - subAccelParam.Time)) *
                    Math.Sin(wd * (accelParam.Time - subAccelParam.Time)) * dt / wd;
                    pret = subAccelParam.Time;
                }
                //添加响应
                this.responseSet.Add(new ResponseParam(accelParam.Time, 
                    (float)disp, (float)(disp * Math.Pow(wn, 2)), (float)(disp * wn)));
            }
        }

        /// <summary>
        /// NewMark积分
        /// </summary>
        /// <param name="DampR"> 阻尼比 </param>
        /// <param name="wn"> 无阻尼圆频率 </param>
        /// <param name="wd"> 带阻尼圆频率 </param>
        /// <param name="pDict">  激励(时间，加速度) </param>
        /// <param name="beta"> Newmark参数 </param>
        /// <param name="gamma"> Newmark参数 </param>
        /// <param name="dt"> 积分步长 </param>
        /// <returns></returns>
        private void Newmark(List<float> paramList)
        {
            //初始化
            this.responseSet = new List<ResponseParam>(this.accelSet.Count);
            //基本参数
            float wn = SpectrumMethod.Getwn(this.Period);
            float wd = SpectrumMethod.Getwd(this.Period, this.DampR);
            float K = this.Mass * (float)Math.Pow(wn, 2);
            float C = 2 * this.Mass * wn * this.DampR;
            //等效刚度
            float KK = K + paramList[0] * this.Mass + paramList[1] * C;
            //初始状态
            float ust = 0; float vst = 0; float ast = 0;
            //初始化参数
            foreach (var accelParam in this.accelSet)
            {
                //防止质量为零
                if(this.Mass == 0)
                {
                    this.responseSet.Add(new ResponseParam(accelParam.Time, 0,0,0));
                    continue;
                }
                var PP = accelParam.Accel * this.Mass + this.Mass * (paramList[0] * ust + paramList[2] * vst + paramList[3] * ast) +
                                C * (paramList[1] * ust + paramList[4] * vst + paramList[5] * ast);
                var u = PP / KK;
                var preu = ust; var prev = vst; var prea = ast;
                ust = u;
                ast = paramList[0] * (u - preu) - paramList[2] * prev - paramList[3] * prea;
                vst = vst + paramList[6] * prea + paramList[7] * ast;
                this.responseSet.Add(new ResponseParam(accelParam.Time, ust, 
                    ast - this.accelSet[this.Count].Accel, vst));
            }
        }

        #endregion

        #region GetMethod

        /// <summary>
        /// 获得最大值
        /// </summary>
        /// <param name="propertyName"></param>
        private float GetMaxValue(string propertyName)
        {
            //获得值列表
            var valueList = this.responseSet.GetPropertyList(propertyName).ConvertAll(f => (float)f);
            //返回最大值
            return Math.Max(Math.Abs(valueList.Max()), Math.Abs(valueList.Min()));
        }

        /// <summary>
        /// SRSS组合
        /// </summary>
        /// <param name="yResponseSet"></param>
        /// <returns></returns>
        public ResponseSet GetSRSSResponse(ResponseSet yResponseSet)
        {
            //响应点数目
            int count = Math.Min(this.Count, yResponseSet.Count);
            //初始化
            var responseSet = new ResponseSet(count, this.Period, this.DampR);
            //添加相应
            for (int i = 0; i < count; i++)
                responseSet.Add(this[i], yResponseSet[i]);
            //返回相应
            return responseSet;
        }

        /// <summary>
        /// 获得对应时刻的响应
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public ResponseParam GetResponseParam(float time)
        {
            //根据周期寻找最近的谱值
            var responses = this.responseSet.Find("Time", time, this.deltaDt * 0.1);
            //是否存在多个值
            if (responses.Count == 0)
            {
                //MessageBoxExtern.Error(string.Format("目标谱周期{0}对应谱值不存在", period));
                return null;
            }
            else if (responses.Count > 1)
                MessageBoxExtern.Error(string.Format("t={0}s存在相似时刻", time));
            return responses.First();
        }

        /// <summary>
        /// 获得影响系数最大值
        /// </summary>
        /// <param name="isPseudo"></param>
        /// <returns></returns>
        public float GetAfaMax(bool isPseudo)
        {
            return isPseudo ? this.Umax * (float)Math.Pow(SpectrumMethod.Getwn(this.Period), 2) : this.Afamax;
        }

        /// <summary>
        /// 速度响应最大值
        /// </summary>
        /// <param name="isPseudo"></param>
        /// <returns></returns>
        public float GetVelocityMax(bool isPseudo)
        {
            return isPseudo ? this.Umax * SpectrumMethod.Getwn(this.Period) : this.Vmax;
        }

        /// <summary>
        /// 获得影响系数最大值（带符号）
        /// </summary>
        /// <param name="isPeseudo"></param>
        /// <returns></returns>
        public float GetAfaMaxWithSignal(bool isPeseudo)
        {
            //获得值列表
            var valueList = this.responseSet.GetPropertyList(isPeseudo ? "U" : "A").ConvertAll(f => (float)f);
            //获得边界值
            var minValue = isPeseudo ? valueList.Min() * (float)Math.Pow(SpectrumMethod.Getwn(this.Period), 2) : valueList.Min();
            var maxValue = isPeseudo ? valueList.Max() * (float)Math.Pow(SpectrumMethod.Getwn(this.Period), 2) : valueList.Max();
            //返回值
            return Math.Abs(minValue) > Math.Abs(maxValue) ? minValue : maxValue;
        }

        /// <summary>
        /// 获得影响系数最大值对应的时刻
        /// </summary>
        /// <param name="isPeseudo"></param>
        /// <returns></returns>
        public float GetAfaMaxTime(bool isPeseudo)
        {
            //获得值列表
            var valueList = this.responseSet.GetPropertyList(isPeseudo ? "U" : "A").ConvertAll(f => (float)f);
            //获得响应最大值（绝对值）
            var maxValue = isPeseudo ? this.Umax : this.Afamax;
            //获得对应标签
            var index = valueList.IndexOf(maxValue);
            return index == -1 ? this[valueList.IndexOf(-maxValue)].Time : this[index].Time;
        }

        /// <summary>
        /// 获得响应数据
        /// </summary>
        /// <returns></returns>
        public List<ResponseParam> GetResponseParams()
        {
            return this.responseSet;
        }

        #endregion

        #region Constructors

        /// <summary>
        /// 构造函数
        /// </summary>
        public ResponseSet()
        {
            this.Mass = 1;
            this.responseSet = new List<ResponseParam>();
            this.accelSet = new List<AccelParam>();
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public ResponseSet(int count, float period, float dampR)
        {
            this.responseSet = new List<ResponseParam>(count);
            this.accelSet = new List<AccelParam>();
            this.Period = period;
            this.DampR = dampR;
            this.Mass = 1;
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accels"></param>
        public ResponseSet(List<AccelParam> accels, List<float> paramList, float period, float dampR)
        {
            this.Mass = 1;
            //获得时程响应
            this.accelSet = accels;
            //获得周期
            this.Period = period;
            this.DampR = dampR;
            //计算时程响应
            this.Newmark(paramList);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accels"></param>
        /// <param name="period"></param>
        /// <param name="dampR"></param>
        public ResponseSet(List<AccelParam> accels, float period, float dampR, float integratDt)
        {
            this.Mass = 1;
            //获得时程响应
            this.accelSet = accels;
            //获得周期
            this.Period = period;
            this.DampR = dampR;
            //计算时程响应
            this.PiecewiseExcitationFunction(integratDt);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accels"></param>
        /// <param name="period"></param>
        /// <param name="dampR"></param>
        public ResponseSet(List<AccelParam> accels, float period, float dampR)
        {
            this.Mass = 1;
            //获得时程响应
            this.accelSet = accels;
            //获得周期
            this.Period = period;
            this.DampR = dampR;
            //计算时程响应
            this.Duhamel();
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accels"></param>
        public ResponseSet(List<AccelParam> accels, List<float> paramList, float period, float mass, float dampR)
        {
            //获得质量
            this.Mass = mass;
            //获得时程响应
            this.accelSet = accels;
            //获得周期
            this.Period = period;
            this.DampR = dampR;
            //计算时程响应
            this.Newmark(paramList);
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="accels"></param>
        /// <param name="period"></param>
        /// <param name="mass"></param>
        /// <param name="dampR"></param>
        /// <param name="integratDt"></param>
        public ResponseSet(List<AccelParam> accels, float period, float mass, float dampR, float integratDt)
        {
            this.Mass = mass;
            //获得时程响应
            this.accelSet = accels;
            //获得周期
            this.Period = period;
            this.DampR = dampR;
            //计算时程响应
            this.PiecewiseExcitationFunction(integratDt);
        }
        #endregion
    }
}
